package com.zhp.helper;

import cn.hutool.json.JSONUtil;
import com.sun.istack.internal.NotNull;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.zhp.constant.RedisFailMsg.*;

/**
 * @author zhanghp
 * @since 2024/5/27 18:07
 */
@Slf4j
@SuppressWarnings("all")
@RequiredArgsConstructor
public class RedisHelper {

    private final RedisTemplate<String, Object> redisTemplate;

    // ===================== 过期时间相关 =========================
    public boolean expire(@NonNull String key, long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    public boolean expire(@NonNull String key, long timeout, TimeUnit timeUnit) {
        if (timeout <= 0) {
            throw new IllegalArgumentException("redis expire timeout has to be greater than 0 s, your expire time is " + timeout);
        }
        try {
            return redisTemplate.expire(key, timeout, timeUnit);
        } catch (Exception e) {
            log.error(format(FAIL_SET_EXPIRE, key, timeout), e);
            return false;
        }
    }

    /**
     * 获取过期时间
     *
     * @param key
     * @return 0-代表永久有效
     */
    public Long getExpire(@NonNull String key) {
        try {
            return redisTemplate.getExpire(key);
        } catch (Exception e) {
            log.error(format(FAIL_GET_EXPIRE, key), e);
            return -1L;
        }
    }

    public Long getExpire(@NonNull String key, TimeUnit timeUnit) {
        try {
            return redisTemplate.getExpire(key, timeUnit);
        } catch (Exception e) {
            log.error(format(FAIL_GET_EXPIRE, key), e);
            return -1L;
        }
    }

    // ========================================== 基本 操作 命令 =========================================================
    public Boolean exists(@NonNull String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error(format(FAIL_GET_EXISTS_KEY, key), e);
            return false;
        }
    }

    public Boolean del(@NonNull String key) {
        try {
            return redisTemplate.delete(key);
        } catch (Exception e) {
            log.error(format(FAIL_DEL_KEY, key));
            return false;
        }
    }

    public Long del(@NonNull Collection<String> keys) {
        try {
            return redisTemplate.delete(keys);
        } catch (Exception e) {
            log.error(format(FAIL_DEL_COLLECTION_KEYS, JSONUtil.toJsonStr(keys)), e);
            return -1L;
        }
    }

    // ============================================ String 字符串 操作 ===================================================

    public void set(@NonNull String key, @NonNull String value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_SET, key, value), e);
        }
    }

    public void set(@NonNull String key, @NonNull String value, long timeout, TimeUnit timeUnit) {
        try {
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_EXPIRE_SET, key, value, timeout), e);
        }
    }

    public void set(@NonNull String key, @NonNull String value, long timeout) {
        try {
            redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_EXPIRE_SET, key, value, timeout), e);
        }
    }

    public String get(@NonNull String key) {
        return (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 递增
     *
     * @param key   key
     * @param delta 要增加几（大于0）
     * @return 递增后的数
     */
    public Long incr(@NonNull String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        try {
            return redisTemplate.opsForValue().increment(key, delta);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_INCR_DELTA, key, delta), e);
        }
    }

    public Long incr(@NonNull String key) {
        try {
            return redisTemplate.opsForValue().increment(key);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_INCR, key), e);
        }
    }

    /**
     * 递减
     *
     * @param key   key
     * @param delta 要减几（小于0）
     * @return 递增后的数
     */
    public Long decr(@NonNull String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        try {
            return redisTemplate.opsForValue().decrement(key, delta);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_DECR_DELTA, key, -delta), e);
        }
    }

    public Long decr(@NonNull String key) {
        try {
            return redisTemplate.opsForValue().decrement(key);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_STRING_DECR, key), e);
        }
    }

    public Boolean setnx(@NonNull String key, @NonNull String value, long timeout) {
        return setnx(key, value, timeout, TimeUnit.SECONDS);
    }

    public Boolean setnx(@NonNull String key, @NonNull String value, long timeout, TimeUnit timeUnit) {
        try {
            return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
        } catch (Exception e) {
            log.error(format(FAIL_STRING_SET_NX_EXPIRE, key, value, timeout), e);
            return false;
        }
    }

    public Boolean setnx(@NonNull String key, @NonNull String value) {
        try {
            return redisTemplate.opsForValue().setIfAbsent(key, value);
        } catch (Exception e) {
            log.error(format(FAIL_STRING_SET_NX, key, value), e);
            return false;
        }
    }

    // ============================================== 哈希 操作 ==========================================================

    public void hset(@NonNull String key, @NonNull String field, Object value) {
        try {
            redisTemplate.opsForHash().put(key, field, value);
        } catch (Exception e) {
            throw new IllegalArgumentException(format(FAIL_HASH_SET, key, field, value), e);
        }
    }

    public void hset(@NonNull String key, @NonNull Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
        } catch (Exception e) {
            throw new IllegalArgumentException(format(FAIL_HASH_SET_COLLECTION, key, JSONUtil.toJsonStr(map)), e);
        }
    }

    public void hset(@NonNull String key, @NonNull Map<String, Object> map, long timeout, TimeUnit timeUnit) {
        if (timeout < 0) {
            throw new IllegalArgumentException("redis expire timeout has to be greater than 0 s, your expire time is " + timeout);
        }
        try {
            redisTemplate.opsForHash().putAll(key, map);
            expire(key, timeout, timeUnit);
        } catch (Exception e) {
            throw new IllegalArgumentException(format(FAIL_HASH_SET_COLLECTION, key, JSONUtil.toJsonStr(map)), e);
        }
    }

    public void hset(@NonNull String key, @NonNull String field, Object value, long timeout, TimeUnit timeUnit) {
        if (timeout < 0) {
            throw new IllegalArgumentException("redis expire timeout has to be greater than 0 s, your expire time is " + timeout);
        }
        try {
            redisTemplate.opsForHash().put(key, field, value);
            expire(key, timeout, timeUnit);
        } catch (Exception e) {
            throw new IllegalArgumentException(format(FAIL_HASH_SET, key, value, timeout), e);
        }
    }

    public Object hget(@NonNull String key, @NonNull String field){
        try {
            return redisTemplate.opsForHash().get(key, field);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_HASH_GET, key, field), e);
        }
    }

    public Map<Object, Object> hgetAll(@NonNull String key){
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_HASH_GET_ALL, key), e);
        }
    }

    public Long hdel(@NonNull String key, @NonNull String ... field){
        try {
            return redisTemplate.opsForHash().delete(key, field);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_HASH_DEL, key, JSONUtil.toJsonStr(field)), e);
        }
    }

    public Boolean hdel(@NonNull String key){
        return this.del(key);
    }

    public Boolean hasHashKey(@NonNull String key, @NonNull String field){
        try {
            return redisTemplate.opsForHash().hasKey(key, field);
        } catch (Exception e) {
            throw new RuntimeException(format(FAIL_HAS_HASH_KEY, key, field), e);
        }
    }

}
